// 原生js工具包
// author: Dary

// ES6字面量对象的增强版的写法： say () {} 相当于  say: function () {}

const utils = {
    /**
     * 获取元素最终样式
     * @param  {DOMObject} ele  要获取样式的对象
     * @param  {String}    key  要获取的样式名
     * @return {String}         获取到的样式值
     */
    getStyle (ele, key) {
        if (ele.currentStyle) {
            return ele.currentStyle[key]
        }
        return getComputedStyle(ele, false)[key]
    },

    /**
     * 添加事件监听
     * @param {DOMObject} ele           要添加事件的元素
     * @param {String}    type          事件名称（不带on）
     * @param {Function}  fn            事件处理函数
     * @param {Boolean}   [isCapture]   是否捕获，默认为false代表冒泡，传true代表捕获
     */
    on (ele, type, fn, isCapture) {
        // isCapture默认值为false
        // isCapture = isCapture === undefined ? false : isCapture
        if (isCapture === undefined) isCapture = false
        if (ele.attachEvent) {
            ele.attachEvent('on' + type, fn)
        } else {
            ele.addEventListener(type, fn, isCapture)
        }
    },

    /**
     * 移除事件监听
     * @param {DOMObject} ele           要添加事件的元素
     * @param {String}    type          事件名称（不带on）
     * @param {Function}  fn            事件处理函数
     * @param {Boolean}   [isCapture]   是否捕获，默认为false代表冒泡，传true代表捕获
     */
    off (ele, type, fn, isCapture) {
        if (isCapture === undefined) isCapture = false
        if (ele.detachEvent) {
            ele.detachEvent('on' + type, fn)
        } else {
            ele.removeEventListener(type, fn, isCapture)
        }
    },

    /**
     * 完成元素的匀速运动
     * @param {DOMObject}  ele       运动的元素 
     * @param {String}     attr      运动的属性名 
     * @param {Number}     end       运动的终点
     * @param {Number}     duration  运动所需总时间，单位ms
     * @param {Function}   [fn]      运动结束以后的回调函数
     */
    move (ele, attr, end, duration, fn) {
        // 先把上一次的定时器清除
        clearInterval(ele.timer)
        // 获取起点值
        var start = parseInt(this.getStyle(ele, attr))
        // 计算距离（为了更加严谨，end取整）
        var distance = parseInt(end) - start
        // 先计算duration有多少步
        var steps = Math.floor(duration / 30)
        // 计算速度：px/步
        var speed = distance / steps
        // 开始运动
        var n = 0 // n记录当前步数
        ele.timer = setInterval(() => {
            // 每往前走一步n++
            n++
            // 当前位置就是起始值加上n个speed
            ele.style[attr] = start + speed * n + 'px'
            // 判断终点
            if (n === steps) {
                // 当运动步数和总步数相等，运动结束
                clearInterval(ele.timer)
                // 避免js的浮点数计算不精确，还是拉到终点
                ele.style[attr] = end + 'px'
                // 回调函数的通用写法：传了才调用，不传就不调用，逻辑短路
                fn && fn()
            }
        }, 30)
    },

    /**
     * 完成元素的缓冲运动
     * @param {DOMObject}  ele       运动的元素 
     * @param {String}     attr      运动的属性名 
     * @param {Number}     end       运动的终点
     * @param {Function}   [fn]      运动结束以后的回调函数
     */
    move1 (ele, attr, end, fn) {
        // 为了眼睛，end取整
        end = parseInt(end)
        // 获取起点值
        var start = parseInt(this.getStyle(ele, attr))
        // 开启定时器
        clearInterval(ele.timer)
        ele.timer = setInterval(() => {
            // 计算剩余距离
            var distance = end - start
            // 计算当前速度：剩下距离的 1/10
            // 负方向得向下取整，正方向向上
            var speed = distance > 0 ? Math.ceil(distance / 10) :  Math.floor(distance / 10)
            // 在这里start不是起始值，是当前值，每次start需要往前走
            // start往前走一步，加一个speed
            start += speed
            ele.style[attr] = start + 'px'
            // 判断终点
            if (start === end) {
                clearInterval(ele.timer)
                fn && fn()
            }
        }, 30)
    },
    /**
     * 获取某个元素到文档边缘的坐标（这个方法不适用于有固定定位的元素）
     * @param {DOMObject} ele 元素对象
     * @return {Object}   返回的坐标 { left, top }
     */
    getOffset (ele) {
        var offset = { top: 0, left: 0 }
        while (ele.offsetParent) {
            offset.top += ele.offsetTop
            offset.left += ele.offsetLeft
            // ele赋值为父级，下一次循环继续算的是父级
            ele = ele.offsetParent
        }
        return offset
    },

    /**
     * 取cookie的方法
     * @param  {string} key 要取的cookie的名称
     * @return {string}     这条cookie的值
     */
    getCookie (key) {
        var str = document.cookie
        var arr = str.split('; ')
        var cookies = arr.reduce((obj, item) => {
            var subArr = item.split('=')
            obj[subArr[0]] = decodeURIComponent(subArr[1])
            return obj
        }, {})
        return cookies[key]
    },

    /**
     * 
     * @param {string} key      要存的cookie的名称
     * @param {string} value    cookie的值
     * @param {object} [option] expires过期时间和path路径(如果不传默认是根路径)，比如：{ expires: 7, path: '/' } 7天过期根目录
     */
    setCookie (key, value, option) {
        var cookie = `${key}=${encodeURIComponent(value)}`
        if (option) {
            if (option.expires) {
                var d = new Date()
                d.setDate(d.getDate() + option.expires)
                cookie += `;expires=${d.toUTCString()}`
            }
            if (option.path) {
                cookie += `;path=${option.path}`
            } else {
                // 传了option但是不传path默认根路径
                cookie += ';path=/'
            }
        } else {
            // option没传，也让path默认根路径
            cookie += ';path=/'
        }
        document.cookie = cookie
    },

    /**
     * 发送ajax get请求
     * @param {string} url 请求的地址
     * @param {object} query 请求要携带的参数
     * @param {function} fn 成功的回调函数
     * @param {boolean} [isJson] 是否json格式，默认为true
     */
    get (url, query, fn, isJson = true) {
        // 参数默认值，ES6新增的，不穿默认为true
        // 先拼接参数
        if (query) {
            url += '?'
            for (var key in query) {
                url += `${key}=${query[key]}&`
            }
            url = url.slice(0, -1)
        }
        var xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.send()
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    var data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText
                    fn && fn(data)
                }
            }
        }
    },

    /**
     * 发送ajax post请求
     * @param {string} url 请求的地址
     * @param {object} query 请求要携带的参数
     * @param {function} fn 成功的回调函数
     * @param {boolean} [isJson] 是否json格式，默认为true
     */
    post (url, query, fn, isJson = true) {
        var str = ''
        if (query) {
            for (var key in query) {
                str += `${key}=${query[key]}&`
            }
            str = str.slice(0, -1)
        }
        var xhr = new XMLHttpRequest()
        xhr.open('POST', url)
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
        xhr.send(str)
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    fn && fn(isJson ? JSON.parse(xhr.responseText) : xhr.responseText)
                }
            }
        }
    },

    /**
     * jsonp请求
     * @param {string} url     请求地址
     * @param {string} cbName  回调函数名
     * @param {object} query   其他的一些参数
     */
    jsonp (url, cbName, query) {
        url += `?cb=${cbName}`
        if (query) {
            for (var key in query) {
                url += `&${key}=${query[key]}`
            }
        }
        // 创建script
        var script = document.createElement('script')
        script.src = url
        document.body.appendChild(script) // 因为是一个异步请求，所以只要script存在请求就发出去了，那就一定会返回
        document.body.removeChild(script) // 过河拆桥
    },
    
    /**
     * 发送基于promise的ajax get请求
     * @param {string} url 请求的地址
     * @param {object} query 请求要携带的参数
     * @param {boolean} [isJson] 是否json格式，默认为true
     */
    fetch (url, query, isJson = true) {
        if (query) {
            url += '?'
            for (var key in query) {
                url += `${key}=${query[key]}&`
            }
            url = url.slice(0, -1)
        }
        // promise我们需要把他return出去，这样将来调用这个方法才能使用then
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest()
            xhr.open('GET', url)
            xhr.send()
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        resolve(isJson ? JSON.parse(xhr.responseText) : xhr.responseText)
                    } else {
                        reject()
                    }
                }
            }
        })
    }
}
